home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_TEXT / Source / shared.subproj / File.m < prev    next >
Text File  |  1995-06-12  |  51KB  |  1,413 lines

  1. /***********************************************************************\
  2. Common class for accessing files in all Convert programs
  3. Copyright (C) 1993 David John Burrowes
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10.  
  11. The author, David John Burrowes, can be reached at:
  12.     davidjohn@kira.net.netcom.com
  13.     David John Burrowes
  14.     1926 Ivy #10
  15.     San Mateo, CA 94403-1367
  16. \***********************************************************************/
  17.  
  18. /*
  19. ====================================================================
  20. This is the implementation file for the File class.  Full documentation for this class can be found in the File.rtf file.  I will not duplicate all that fine information here.
  21.     This is $Revision: 1.8 $ of this file
  22.     It was last modified by $Author: death $ on $Date: 93/04/04 23:44:26 $
  23. Note that this file was created while using the New Century Schoolbook Roman typeface.  You may find that some things line up strangely if you don't use that family.
  24.  * $Log:    File.m,v $
  25. Revision 1.8  93/04/04  23:44:26  death
  26. Sun Apr  4 23:44:26 PDT 1993
  27.  
  28. Revision 1.7  93/01/10  15:07:54  death
  29. Sun Jan 10 15:07:54 PST 1993
  30.  
  31. Revision 1.6  92/07/26  13:58:06  death
  32. Update so all works with the font converter...
  33.  
  34.  
  35. Revision 1.4  92/04/05  22:51:36  death
  36. Miscelaneous revisions.  This is the last version of version 1.
  37.  
  38. Revision 1.3  92/03/29  12:36:14  death
  39. Bug in the open method. If we failed, we returned OK, if we succed, we reported failure.
  40.  
  41. Revision 1.2  92/03/29  12:29:06  death
  42. Oops.  Managed to check in the wrong version.  this one has the code reflecting the new location of the init methods 
  43.  
  44. Revision 1.1  92/03/29  12:18:44  death
  45. Initial revision
  46.  
  47.  *====================================================================
  48. */
  49.  
  50. //
  51. //    Import our own definition
  52. //
  53. #import "File.h"
  54.  
  55. #import <stdio.h>    
  56. #import <string.h>
  57. #import <stdlib.h>
  58. #import <sys/param.h>    // for maxpathlen
  59. #import <libc.h>            // for getwd
  60. #import <sys/file.h>        // for open()
  61. #import    <streams/streams.h>    // for streams stuff (exception codes)
  62. #include <sys/time.h>        // for gettimeofday
  63. #if (NSmajor == 3)
  64. #    import    <errno.h>
  65. #endif
  66.  
  67. @implementation File
  68.  
  69. //
  70. //    @@@ bugs... doesn't deal with links properly.  =(
  71. //
  72.  
  73. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  74. //    Routine:        CreateAndOpenFor
  75. //    Parameters:    the means of accessing the specified file
  76. //    Returns:        self
  77. //    Stores:        none
  78. //    Description:
  79. //        This method will try to create a new file and open it.  The file is presumed
  80. //        to be the one whose path is stored in the instance variables of this object.
  81. //        If the file already exists, or if an error occurrs when trying to creat it,
  82. //        an error is stored.  Otherwise the file is opened, and the object is ready for use.
  83. //    Bugs:
  84. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  85. - CreateAndOpenFor: (AccessType) access
  86. {
  87.     //
  88.     //    If the object is working with a file already give an error.
  89.     //    otherwise, do our stuff.
  90.     //
  91.     if (TheFile != NULL)
  92.         [self StoreErrorCode: ERR_SOMEFILEOPEN
  93.             AndText: "Object already in use for another file."];
  94.     {
  95.         if ([self FileExists: FileName] == YES)
  96.             [self StoreErrorCode: ERR_FILEEXISTS
  97.                 AndText: "Can't create the requested file. It already exists."];
  98.         else
  99.         {
  100.             [self CreateFile: FileName];
  101.             if ([self GetErrorCode] != ERR_OK)
  102.                 [self StoreErrorCode: ERR_CREATEFAIL
  103.                     AndText: "Can't create the requested file."];
  104.             else
  105.                 [self OpenWithAccess: access];
  106.                 // Error info stored by this method.
  107.         }
  108.     }
  109.     return self;
  110. }
  111.  
  112. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  113. //    Routine:        OpenExistingFor
  114. //    Parameters:    the means of accessing the specified file
  115. //    Returns:        self
  116. //    Stores:        none
  117. //    Description:
  118. //        This method will try to open an existing file.  The file is presumed
  119. //        to be the one whose path is stored in the instance variables of this object.
  120. //        If the file does not exist,  an error is stored. Otherwise the file is opened,
  121. //        and the object is ready for use.
  122. //    Bugs:
  123. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  124. - OpenExistingFor: (AccessType) access
  125. {
  126.     //
  127.     //    If the object is working with a file already give an error.
  128.     //    otherwise, do our stuff.
  129.     //
  130.     if (TheFile != NULL)
  131.         [self StoreErrorCode: ERR_SOMEFILEOPEN
  132.             AndText: "Object already in use for another file."];
  133.     {
  134.          if ([self FileExists: FileName] == NO)
  135.             [self StoreErrorCode: ERR_FILEDOESNTEXIST
  136.                 AndText: "Can't locate the requested file."];
  137.         else
  138.             [self OpenWithAccess: access];
  139.             // Error info stored by this method.
  140.     }
  141.     return self;
  142. }
  143.  
  144.  
  145. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  146. //    Routine:        ClearAndOpenExistingFor
  147. //    Parameters:    the means of accessing the specified file
  148. //    Returns:        self
  149. //    Stores:        none
  150. //    Description:
  151. //        This method will try to open an existing fil, and then clear (truncate) it.
  152. //        The file is presumed to be the one whose path is stored in the instance
  153. //        variables of this object. If the file does not exist, or if an error occurrs while
  154. //        clearing it,  an error is stored. Otherwise the file is opened,
  155. //        and the object is ready for use.
  156. //    Bugs:
  157. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  158. - ClearAndOpenExistingFor: (AccessType) access
  159. {
  160.     //
  161.     //    If the object is working with a file already give an error.
  162.     //    otherwise, do our stuff.
  163.     //
  164.     if (TheFile != NULL)
  165.         [self StoreErrorCode: ERR_SOMEFILEOPEN
  166.             AndText: "Object already in use for another file."];
  167.     {
  168.          if ([self FileExists: FileName] == NO)
  169.             [self StoreErrorCode: ERR_FILEDOESNTEXIST
  170.                 AndText: "Can't locate the requested file."];
  171.         else
  172.         {
  173.             [self ClearFile: FileName];
  174.             if ([self GetErrorCode] != ERR_OK)
  175.                 [self StoreErrorCode: ERR_CLEARFAIL
  176.                     AndText: "Can't clear the file!"];
  177.             else
  178.                 [self OpenWithAccess: access];
  179.                 // Error info stored by this method.
  180.         }
  181.     }
  182.     return self;
  183. }
  184.  
  185.  
  186. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  187. //    Routine:        ClearAndOpenFor
  188. //    Parameters:    the means of accessing the specified file
  189. //    Returns:        self
  190. //    Stores:        none
  191. //    Description:
  192. //        This method will try to open a file.  If it doesn't exist already, it will create it.
  193. //        if it does, then it will clear it (truncate).  The file is then opened if all went well
  194. //        The file is presumed to be the one whose path is stored in the instance
  195. //        variables of this object. 
  196. //    Bugs:
  197. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  198. - ClearAndOpenFor: (AccessType) access
  199. {
  200.     //
  201.     //    If the object is working with a file already give an error.
  202.     //    otherwise, do our stuff.
  203.     //
  204.     if (TheFile != NULL)
  205.         [self StoreErrorCode: ERR_SOMEFILEOPEN
  206.             AndText: "Object already in use for another file."];
  207.     {
  208.          if ([self FileExists: FileName] == NO)
  209.         {
  210.             [self CreateFile: FileName];
  211.             if ([self GetErrorCode] != ERR_OK)
  212.                 [self StoreErrorCode: ERR_CREATEFAIL
  213.                     AndText: "Can't create the requested file."];
  214.         }
  215.         else
  216.         {
  217.             [self ClearFile: FileName];
  218.             if ([self GetErrorCode] != ERR_OK)
  219.                 [self StoreErrorCode: ERR_CLEARFAIL
  220.                     AndText: "Can't clear the file!"];
  221.         }
  222.         //
  223.         //    The file exists and is of 0 length.  open it now.
  224.         //
  225.         if ([self GetErrorCode] == ERR_OK)
  226.             [self OpenWithAccess: access];
  227.     }
  228.     return self;
  229. }
  230.  
  231.  
  232. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  233. //    Routine:        OpenFor
  234. //    Parameters:    the means of accessing the specified file
  235. //    Returns:        self
  236. //    Stores:        none
  237. //    Description:
  238. //        This method will try to open a file.  If it doesn't exist already, it will create it.
  239. //        The file is then opened if all went well.
  240. //        The file is presumed to be the one whose path is stored in the instance
  241. //        variables of this object. 
  242. //    Bugs:
  243. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  244. - OpenFor: (AccessType) access
  245. {
  246.     //
  247.     //    If the object is working with a file already give an error.
  248.     //    otherwise, do our stuff.
  249.     //
  250.     if (TheFile != NULL)
  251.         [self StoreErrorCode: ERR_SOMEFILEOPEN
  252.             AndText: "Object already in use for another file."];
  253.     {
  254.          if ([self FileExists: FileName] == NO)
  255.         {
  256.             [self CreateFile: FileName];
  257.             if ([self GetErrorCode] != ERR_OK)
  258.                 [self StoreErrorCode: ERR_CREATEFAIL
  259.                     AndText: "Can't create the requested file."];
  260.         }
  261.         else
  262.                 [self StoreErrorCode: ERR_OK  AndText: "File exists.  all is peachy."];
  263.         //
  264.         //    The file exists.  open it now.
  265.         //
  266.         if ([self GetErrorCode] == ERR_OK)
  267.             [self OpenWithAccess: access];
  268.     }
  269.     return self;
  270. }
  271.  
  272.  
  273.  
  274. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  275. //    Routine:        OpenWithAccess:
  276. //    Parameters:    the means of accessing the specified file
  277. //    Returns:        self
  278. //    Stores:        none
  279. //    Description:
  280. //        This is a lower level opening routine.  It should probably never be called from
  281. //        the outside, but instead be called by the (presently) five opening routines
  282. //        above this in the listing.
  283. //        It's purpose is simple: open the file that this object is going to work with.
  284. //        If the file does'nt exist, it returns an error.  Thus, it is up to the caller to
  285. //        assure that the file exists and is in th proper form before calling this.
  286. //    Bugs:
  287. //    Not accounting for the fact that I may not have *permission* (666 kinda thing)
  288. //    to do any of these...
  289. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  290. - OpenWithAccess: (AccessType) operation
  291. {
  292.     struct stat *statBuf;
  293.     [self ResetResults];
  294.     
  295.     //
  296.     //    Make sure that the file exists, and that we aren't already working with
  297.     //    an opened file.  If all is good, open it appropriately
  298.     //
  299.     if (([self FileExists: FileName] == YES) && (TheFile == NULL))
  300.     {
  301.         switch (operation)
  302.         {
  303.             case FILE_READ:
  304.                 TheFile = NXMapFile(FileName, NX_READONLY);
  305.                 break;
  306.             case FILE_WRITE:
  307.                 TheFile = NXMapFile(FileName, NX_WRITEONLY);
  308.                 break;
  309.             case    FILE_READWRITE:
  310.                 TheFile = NXMapFile(FileName, NX_READWRITE);
  311.                 break;
  312.             case FILE_APPEND:
  313.                 TheFile = NXMapFile(FileName, NX_WRITEONLY);
  314.                 //
  315.                 //    Appending must be at the end of the file...
  316.                 //    @@@ do we need + 1 here?
  317.                 //
  318.                 [self MoveTo: [self FileSize]];
  319.                 break;
  320.             default:
  321.                 [self StoreErrorCode: ERR_BADACCESS
  322.                     AndText: "An invalid access operation was requested. No file opened."];
  323.                 break;
  324.         }
  325.     }
  326.     //
  327.     //    If the file wasn't opened, or some other problem arose, generate a negative reply.
  328.     //
  329.     if ((TheFile == NULL) && ([self GetErrorCode] != ERR_BADACCESS))
  330.         [self StoreErrorCode: ERR_CANTOPEN
  331.                 AndText: "The specified file could not be opened (bad permissions?  Bad path?)"];
  332.     else
  333.     {
  334.         AccessMode = operation;
  335.         //
  336.         //    Store the real inode now, in case we could not get it during the init.
  337.         //
  338.         [self    FileInfo];
  339.         if ([self GetErrorCode] != ERR_OK)
  340.             TheInode = 0;
  341.         else
  342.         {
  343.             statBuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT];
  344.             TheInode = statBuf->st_ino;
  345.         }
  346.     }
  347.     //
  348.     //    Store whether the file was opened or not
  349.     //
  350.     if ((TheFile != NULL) && ([self GetErrorCode] == ERR_OK))
  351.     {
  352.         FileIsOpen = YES;
  353.         FileLocation = fileAtStart;
  354.     }
  355.  
  356.     return self;
  357. }
  358.  
  359.  
  360. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  361. //    Routine:        initAndUse:
  362. //    Parameters:    none
  363. //    Returns:        self
  364. //    Stores:        none
  365. //    Description:
  366. //        This funky method is a pretty standard init method.  It sets up all our instance
  367. //        variables, and does nothing more.
  368. //    Bugs:
  369. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  370. - initAndUse: (roCString) pathname
  371. {
  372.     struct stat *statbuf;
  373.     
  374.     [super init];
  375.     FileName = [self GetFullPathFrom: pathname];
  376.     AccessMode = FILE_NOACCESS;
  377.     TheFile = NULL;
  378.     //
  379.     //    Get our inode, if possible.
  380.     //
  381.     [self    FileInfo];
  382.     if ([self GetErrorCode] != ERR_OK)
  383.     {
  384.         //  @@@ Maybe this shouldn't be this way...
  385.         [self   StoreErrorCode: ERR_OK  AndText: "Hmmmmmm"];
  386.         TheInode = 0;
  387.     }
  388.     else
  389.     {
  390.         statbuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT];
  391.         TheInode = statbuf->st_ino;
  392.     }
  393.  
  394.     FileIsOpen = NO;
  395.     FileLocation = fileAtStart;
  396.  
  397.     return self;
  398. }
  399.  
  400.  
  401. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  402. //    Routine:        initAndUseTemporary
  403. //    Parameters:    none
  404. //    Returns:        self
  405. //    Stores:        none
  406. //    Description:
  407. //        This method is used when one wishes to have a temporary file to use.
  408. //        Calling this method will cause a temporary file to be created somwhere.
  409. //        one can then use it as a completely ordinary file with this object.
  410. //    Bugs:
  411. //        At the moment, this is implemented with the tmpname call.  Because we
  412. //        ignore the TMP_MAX constant (the object shouldn't be tracking how many
  413. //        temp files are open in the program, and the program shouldn't know we're
  414. //        making use of tmpnam), we run the risk of opening too many temp files.
  415. //        Thus, this should be re-implemented ourselves sometime (perhaps to:
  416. //        /tmp/FileObj_TempXXXXXXXX, where X is a number-thing that will actually
  417. //        be a date from a date object, or some something more unique...
  418. //    History
  419. //        93.01.03    djb    Added call to stat, because it seems you have to call tmpnam repeatedly
  420. //                    to get your unique name.  YICK!  Please move to the above.  Then
  421. //                    replaced with intermediate hack using a pretty unique name using the
  422. //                    above and the process id, seconds since 1970, and microseconds.
  423. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  424. - initAndUseTemporary
  425. {
  426.     struct timeval tp;
  427.     struct timezone tzp;
  428.     [super init];
  429.  
  430.     FileName = NewCString(63);
  431.     gettimeofday(&tp, &tzp);
  432.     //    93.01.24    djb    Added the 'long' designation to the %d's at the NS3 compiler's suggestion.
  433.     sprintf(FileName, "/tmp/TempFileDataP%dS%ldU%ld",
  434.         getpid(), tp.tv_sec, tp.tv_usec);
  435.  
  436. /*
  437.     This approach does not work.  Nor do previsu simpler ones.
  438.     FileName = NewCString(L_tmpnam);
  439.     ctr = 0;
  440.     do
  441.     {
  442.         FileName = tmpnam(FileName);
  443.         result = stat(FileName, &mystat);
  444.         ctr++;
  445.     }
  446.     while ((errno != 2) && (ctr < TMP_MAX));
  447. */
  448.  
  449.     AccessMode = FILE_NOACCESS;
  450.     TheFile = NULL;
  451.     FileIsOpen = NO;
  452.     FileLocation = fileAtStart;
  453.     return self;
  454. }
  455.  
  456.  
  457. //============================================================
  458. //============================================================
  459.  
  460.  
  461. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  462. //    Routine:        ClearFile:
  463. //    Parameters:    a CString that contains a path to a file
  464. //    Returns:        self
  465. //    Stores:        none
  466. //    Description:
  467. //        This little kludgey method just clears the specified file.  If anything bad
  468. //        happens, it stores an error code and text.  At the moment, it truncates the file
  469. //        by opening it with the O_TRUNC flag to the open() routine.  yech.  This
  470. //        implementation had BETTER remain transparent to this and all subclasses,
  471. //        for it's sure to change at some point!
  472. //    Bugs:
  473. //        Ickly implementation.
  474. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  475. - ClearFile: (CString) pathname
  476. {
  477.     int fd;
  478.     
  479.     errno = 0;
  480.     fd =open(pathname, O_RDONLY|O_TRUNC, 666);
  481.     if (errno != 0)
  482.         [self StoreErrorCode: ERR_CLEARFAIL AndText: "The open() routine didn't O_TRUNC it!"];
  483.     close (fd);
  484.     return self;
  485. }
  486.  
  487.  
  488. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  489. //    Routine:        CreateFile:
  490. //    Parameters:    a CString that contains a path to a file
  491. //    Returns:        self
  492. //    Stores:        none
  493. //    Description:
  494. //        This little kludgey method just creates the specified file.  If anything bad
  495. //        happens, it stores an error code and text.  At the moment, it creates the file
  496. //        by opening it with the O_CREAT flag to the open() routine.  yech.  This
  497. //        implementation had BETTER remain transparent to this and all subclasses,
  498. //        for it's sure to change at some point!
  499. //    Bugs:
  500. //        Ickly implementation.
  501. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  502. - CreateFile: (CString) pathname
  503. {
  504.     int fd;
  505.     
  506.     errno = 0;
  507.     fd =open(pathname, O_RDONLY|O_CREAT, 0666);
  508.     if (errno != 0)
  509.         [self StoreErrorCode: ERR_CREATEFAIL AndText: "The open() routine didn't create it!"];
  510.     else
  511.         [self StoreErrorCode: ERR_OK AndText: "The file was created"];
  512.     close (fd);
  513.     return self;
  514. }
  515.  
  516.  
  517. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  518. //    Routine:        FileExists: 
  519. //    Parameters:    a CString that contains a path to a file
  520. //    Returns:        True if the file exists
  521. //    Stores:        the return value
  522. //    Description:
  523. //        This method has only one purpose: it returns YES if the specified file
  524. //        exists, and NO otherwise.  We determine this by whether stat() reports no
  525. //        errors when trying to get info about the file.
  526. //    Bugs:
  527. //        Also, we always return an ERR_OK value...  certianly *something* can go wrong?
  528. //        stat() is sensitifive to 8 bit character names.  All I can say is that I find this
  529. //        to be an unacceptable drawback.  However, I haven't the time or energy right
  530. //        now to get around this (and for all I know, the doc is just wrong).  
  531. //    History
  532. //        93.07.18    djb    Revised to use stat(), finally!
  533. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  534. - (Boolean) FileExists: (CString) pathname
  535. {
  536.     struct stat statBuf;
  537.     Integer    statCode;
  538.  
  539.     statCode = stat(pathname, &statBuf);
  540.     if (statCode != 0)
  541.     {
  542.         [self StoreErrorCode: ERR_OK AndText: "The file does not exist or can not be reached"];
  543.         return NO;
  544.     }
  545.     else
  546.     {
  547.         [self StoreErrorCode: ERR_OK AndText: "The file does indeed exist"];
  548.         return YES;
  549.     }
  550. }
  551.  
  552.  
  553. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  554. //    Routine:        GetFullPathFrom
  555. //    Parameters:    a CString that contains a full or partial path to a file
  556. //    Returns:        a new CString containing the full path to the file
  557. //    Stores:        the new CString
  558. //    Description:
  559. //        The purpose of this string is to take a full or partial pathname (i.e.
  560. //        /me/Library/file.ext  or Library/file.ext or file.ext), and return the full
  561. //        pathname to the file.
  562. //        If we were passed a full pathname (starts with the / character), then we
  563. //        make a full copy of it.
  564. //        otherwise, use the function getwd to get the path to the current directory,
  565. //        and fuse the two together.
  566. //        Note that we do not reset the results here.  Since this is an internal
  567. //        method, the caller, in the object, may not want things cleared.
  568. //    Bugs:
  569. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  570. - (CString) GetFullPathFrom: (roCString) pathname
  571. {
  572.     char    rootedpath[MAXPATHLEN];
  573.     CString    fullpath;
  574.     char*    pathresult;
  575.  
  576.     //
  577.     //    If the path is a complete path, then just copy it literally.
  578.     //
  579.     if (pathname[0] == '/')
  580.     {
  581.         fullpath = (CString) malloc(strlen(pathname) + 1);
  582.         strcpy(fullpath, pathname);
  583.         [self StoreErrorCode: ERR_OK AndText: "Got filename!"];
  584.     }
  585.     else
  586.     {
  587.         //
  588.         //    Otherwise, we have a partial path.  Get path to our current location
  589.         //    (presumably where the partial path starts from), and fuse the two
  590.         //    (if getwd() returns an error, something is wrong, and so we risk returning
  591.         //    just the partial path)
  592.         //
  593.          pathresult = getwd(rootedpath);
  594.         if (*pathresult != 0)
  595.         {
  596.             fullpath = (CString) malloc(strlen(rootedpath)+1+strlen(pathname) + 1);
  597.             sprintf(fullpath, "%s/%s", rootedpath, pathname);
  598.             [self StoreErrorCode: ERR_OK AndText: "Got filename!"];
  599.         }
  600.         else
  601.         {
  602.             fullpath = (CString) malloc(strlen(pathname) + 1);
  603.             strcpy(fullpath, pathname);
  604.             [self StoreErrorCode: ERR_STRANGEPATH AndText: "Pathname may be corrupted!"];
  605.         }
  606.     }
  607.     return fullpath;
  608. }
  609.  
  610. //============================================================
  611. //============================================================
  612.  
  613.  
  614. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  615. //    Routine:        Close
  616. //    Parameters:    a flag to indicate whether we should save the file
  617. //    Returns:        self
  618. //    Stores:        none
  619. //    Description:
  620. //        This closes the file, discarding any changes.
  621. //    Bugs:
  622. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  623. - Close
  624. {
  625.     [self ResetResults];
  626.  
  627.     if (TheFile != NULL)
  628.     {
  629.         NXCloseMemory(TheFile, NX_FREEBUFFER);
  630.         [self StoreErrorCode: ERR_OK AndText: "Closed right up"];
  631.     }
  632.     else
  633.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"];
  634.  
  635.     FileIsOpen = NO;
  636.     FileLocation = fileAtStart;
  637.  
  638.     return self;
  639. }
  640.  
  641.  
  642. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  643. //    Routine:        CloseAndSave
  644. //    Parameters:    none
  645. //    Returns:        self
  646. //    Stores:        none
  647. //    Description:
  648. //        This closes the file,saving its contents before doing so.
  649. //    Bugs:
  650. //        how about some checking to make sure we dumped it all OK??
  651. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  652. - CloseAndSave
  653. {
  654.     [self ResetResults];
  655.  
  656.     if (TheFile != NULL)
  657.     {
  658.         if (AccessMode != FILE_READ)
  659.             NXSaveToFile(TheFile, FileName);
  660.         NXCloseMemory(TheFile, NX_FREEBUFFER);
  661.         [self StoreErrorCode: ERR_OK AndText: "Closed right up"];
  662.     }
  663.     else
  664.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"];
  665.  
  666.     FileIsOpen = NO;
  667.     FileLocation = fileAtStart;
  668.  
  669.     return self;
  670. }
  671.  
  672.  
  673. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  674. //    Routine:        CloseAndDelete
  675. //    Parameters:    none
  676. //    Returns:        self
  677. //    Stores:        the error we got back from the system routine.  (don't depend on this)
  678. //    Description:
  679. //        This closes the file, and then deletes it.
  680. //    Bugs:
  681. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  682. - CloseAndDelete
  683. {
  684.     Integer status;
  685.     [self ResetResults];
  686.     
  687.     [self Close];
  688.     status = remove(FileName);
  689.     if (status != 0)
  690.     {
  691.         [self StoreErrorCode: ERR_CANTDELETE AndText: "Could not delete file. unknown why"];
  692.         [self StoreInteger: status];
  693.     }
  694.     else
  695.         [self StoreErrorCode: ERR_OK AndText: "Deleted the file just fine"];
  696.  
  697.     FileIsOpen = NO;
  698.     FileLocation = fileAtStart;
  699.  
  700.     return self;
  701. }
  702.  
  703.  
  704. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  705. //    Routine:        free:
  706. //    Parameters:    none
  707. //    Returns:        self
  708. //    Stores:        none
  709. //    Description:
  710. //        This frees the object, which involves closing our connection to the physical
  711. //        file, WITHOUT SAVING, and then disposing of our instance variables,
  712. //        and then freeing our parent.
  713. //    Bugs:
  714. //        The call to close is commented out here because it's causing trouble if you
  715. //        say: [myFile CloseAndSave] ;  [myFile free];  Streams problem.  we SHOULD
  716. //        be somehow nulling the stream, in the Close methods, and closing here only if
  717. //        it isn't null.
  718. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  719. - free
  720. {
  721.     //[self Close];
  722.     free(FileName);
  723.     return [super free];
  724. }
  725.  
  726.  
  727. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  728. //    Routine:        AdvanceBytes:
  729. //    Parameters:    the number of bytes we should advance
  730. //    Returns:        self
  731. //    Stores:        none
  732. //    Description:
  733. //        This changes our current position in the file by advancing us up a specified number
  734. //        of bytes in it. We add an exception handler partially for the ease so we don't
  735. //        have to do the error checking ourselves.  Partially because it allows us to
  736. //        check for other exceptions and kinda trap for them.  And partially for the
  737. //        novelty of it.
  738. //    Bugs:
  739. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  740. - AdvanceBytes: (FilePosDelta) byteLoc;
  741. {
  742.     [self ResetResults];
  743.  
  744.     if (TheFile == NULL)
  745.         [self StoreErrorCode: ERR_FILENOTOPEN  AndText: "The file wasn't even open!"];
  746.     else
  747.     {
  748.         //
  749.         //    Try to advance the requested number of bytes.  If an exception occurrs,
  750.         //    and it's a seek error, just stick us at the end quietly.  Otherwise, store
  751.         //    an error code and leave things as they are.  
  752.         //
  753.         NX_DURING
  754.             NXSeek(TheFile, byteLoc, NX_FROMCURRENT);
  755.             [self StoreErrorCode: ERR_PEACHY AndText: "We found what we NXsaught."];
  756.         NX_HANDLER
  757.             switch(NXLocalHandler.code)
  758.             {
  759.                 case NX_illegalSeek:
  760.                     NXSeek(TheFile, 0, NX_FROMEND);
  761.                     [self StoreErrorCode: ERR_OK AndText: "Moved to the end."];
  762.                     FileLocation = fileAtEOF;
  763.                     break;
  764.                 default :
  765.                     [self StoreErrorCode: ERR_BADADVANCE
  766.                         AndText: "A unknown exception occurred when advancing."];
  767.                     break;
  768.             }
  769.         NX_ENDHANDLER
  770.     }
  771.     return self;
  772. }
  773.  
  774.  
  775.  
  776. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  777. //    Routine:        BackupBytes:
  778. //    Parameters:    the number of bytes we should backup
  779. //    Returns:        self
  780. //    Stores:        none
  781. //    Description:
  782. //        This changes our current position in the file by backing us up a specified number
  783. //        of bytes in it.  We set up an exception handler to deal with the case if the
  784. //        user tries to seek before the beginning.  We could, perhaps, do the traping
  785. //        ourselves (compute current loc, distance from beginning, whether the byteLoc
  786. //        falls within that request... etc), but this seems easier, and deals with any other
  787. //        unexpected exceptions too.
  788. //    Bugs:
  789. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  790. - BackupBytes: (FilePosDelta) byteLoc;
  791. {
  792.     [self ResetResults];
  793.  
  794.     if (TheFile == NULL)
  795.         [self StoreErrorCode: ERR_FILENOTOPEN  AndText: "The file wasn't even open!"];
  796.     else
  797.     {
  798.         //
  799.         //    Try to backup the requested number of bytes.  If an exception occurrs,
  800.         //    and it's a seek error, just stick us at the beginning quietly.  Otherwise, store
  801.         //    an error code and leave things as they are.  
  802.         //
  803.         NX_DURING
  804.             NXSeek(TheFile, -byteLoc, NX_FROMCURRENT);
  805.             [self StoreErrorCode: ERR_OK AndText: "We found what we NXsaught."];
  806.         NX_HANDLER
  807.             switch(NXLocalHandler.code)
  808.             {
  809.                 case NX_illegalSeek:
  810.                     NXSeek(TheFile, 0, NX_FROMSTART);
  811.                     [self StoreErrorCode: ERR_OK AndText: "Moved to beginning."];
  812.                     FileLocation = fileAtEOF;
  813.                     break;
  814.                 default :
  815.                     [self StoreErrorCode: ERR_BADBACKUP
  816.                         AndText: "A unknown exception occurred when backing up."];
  817.                     break;
  818.             }
  819.         NX_ENDHANDLER
  820.     }
  821.     return self;
  822. }
  823.  
  824.  
  825. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  826. //    Routine:        GetBasename
  827. //    Parameters:    none
  828. //    Returns:        the copy of a string containing the current filename's basename
  829. //    Stores:        A pointer to the same string we return
  830. //    Description:
  831. //        This simply  extracts the basename of the current file name.  The basename is
  832. //        the part of the name that comes after thedirectory names, but before the extension.
  833. //        If we are using the file  /foo/bar/hotfile.rtf, this returns hotfile
  834. //    Bugs:
  835. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  836. - (CString) GetBasename
  837. {
  838.     CString    lastSlash, lastDot, tempName, finish, start;
  839.     Integer    nameSize;
  840.     [self ResetResults];
  841.     //
  842.     //    Determine the length of the basename by figuring out where its start is
  843.     //    (either right after the /, or at the start) and the end (before . or at the end)
  844.     //
  845.     lastSlash = strrchr(FileName, '/');
  846.     lastDot = strrchr(FileName, '.');
  847.     if (lastSlash == NullCString)
  848.         start = FileName;
  849.     else
  850.         start = &lastSlash[1];
  851.     
  852.     if ((lastDot == NullCString) || (lastDot < lastSlash))
  853.         finish = &FileName[strlen(FileName)];
  854.     else
  855.         finish = lastDot;
  856.  
  857.     nameSize = finish - start;
  858.     //
  859.     //    Allocate the proper space for the basename, store the text, and return it.
  860.     //
  861.     tempName = (CString) malloc(nameSize+1);
  862.     strncpy(tempName, start, nameSize);
  863.     tempName[nameSize] = EndOfCString;
  864.     
  865.     [self StoreErrorCode: ERR_OK AndText: "Located whatever basename was to be found."];
  866.     [self StoreCString: tempName];
  867.     return tempName;
  868. }
  869.  
  870.  
  871. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  872. //    Routine:        GetCurrentPosition
  873. //    Parameters:    none
  874. //    Returns:        A position in the file
  875. //    Stores:        A position in the file as an Integer
  876. //    Description:
  877. //        this returns a FilePos value which indicates a current position in the file, as
  878. //        a count of the number of bytes since the beginning.
  879. //    Bugs:
  880. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  881. - (FilePos) GetCurrentPosition
  882. {
  883.     FilePos    pos;
  884.     [self ResetResults];
  885.     if (TheFile == NULL)
  886.     {
  887.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No position in an unopened file."];
  888.         pos =  0;
  889.     }
  890.     else
  891.     {
  892.         pos =  NXTell(TheFile);
  893.         [self StoreErrorCode: ERR_OK AndText: "Got the file position."];
  894.     }
  895.     [self StoreInteger: pos];
  896.     return pos;
  897. }
  898.  
  899.  
  900. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  901. //    Routine:        GetExtension
  902. //    Parameters:    none
  903. //    Returns:        the copy of a string containing the current filename's extension
  904. //    Stores:        A pointer to the same string we return
  905. //    Description:
  906. //        This simply  extracts the extension of the current file name.  The extension is
  907. //        the part of the name that comes after the last . in the filename.  If there is no
  908. //        extension, this will simply return an empty string.
  909. //        If we are using the file  /foo/bar/hotfile.rtf, this returns rtf
  910. //    Bugs:
  911. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  912. - (CString) GetExtension
  913. {
  914.     //
  915.     //    Determine where the last / and . are. We need the / to be sure we don't get a . further
  916.     //    up in the path somewhere.
  917.     //
  918.     CString    lastSlash, lastDot, start, tempName;
  919.  
  920.     [self ResetResults];
  921.     //
  922.     //     Locate the last . in the file name, and the last /.  If we found no dot, or it came
  923.     //    before the slash in the name, then there is no extension, so start with a null CString.
  924.     //    otherwise, set the start of the exension to just after the last dot.
  925.     //    
  926.     lastSlash = strrchr(FileName, '/');
  927.     lastDot = strrchr(FileName, '.');
  928.     if ((lastDot == NullCString) || (lastDot < lastSlash))
  929.         start = NullCString;
  930.     else
  931.         start = &lastDot[1];
  932.     //
  933.     //    Allocate space for the extension in a temporary string, and copy it into
  934.     //    TempName.
  935.     //
  936.     tempName = (CString) malloc(strlen(start)+1);
  937.     strcpy(tempName, start);
  938.     [self StoreErrorCode: ERR_OK AndText: "Located whatever extension was to be found."];
  939.     [self StoreCString: tempName];
  940.     return tempName;
  941. }
  942.  
  943.  
  944. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  945. //    Routine:        GetFilename
  946. //    Parameters:    none
  947. //    Returns:        the copy of a string containing the full filename, without 
  948. //    Stores:        A pointer to the same string we return
  949. //    Description:
  950. //    this returns the path to the file that we are using without including the file name
  951. //    itselfor the slash before the filename.  If there are no /'s (and thus, oddly, no directories
  952. //    refered to), a null string is returned. If we are using the file  /foo/bar/hotfile.rtf, this
  953. //    returns hotfile.rtf
  954. //    Bugs:
  955. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  956. - (CString) GetFilename
  957. {
  958.     CString    start;
  959.     CString    tempName;
  960.     [self ResetResults];
  961.     //
  962.     //    Locate the slash before the Filename, and make start point after it
  963.     //    (which happens to be the start of our filename)
  964.     //
  965.     start = strrchr(FileName, '/');
  966.     if (start != NullCString)
  967.         start = &start[1];
  968.     else
  969.         start = FileName;
  970.     //
  971.     //    Allocate the proper space for the filename, copy the text, and return it.
  972.     //
  973.     tempName = (CString) malloc(strlen(start)+1);
  974.     strcpy(tempName,  start);
  975.     [self StoreErrorCode: ERR_OK  AndText: "Extracted the filename"];
  976.     [self StoreCString: tempName];
  977.     return tempName;
  978. }
  979.  
  980.  
  981. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  982. //    Routine:        GetDirectory
  983. //    Parameters:    none
  984. //    Returns:        the copy of a string containing the pathname to this directory
  985. //    Stores:        A pointer to the same string we return
  986. //    Description:
  987. //    this returns the path to the file that we are using without including the file name
  988. //    itselfor the slash before the filename.  If there are no /'s (and thus, oddly, no directories
  989. //    refered to), a null string is returned.  If we are using the filen  /foo/bar/hotfile.rtf, this returns
  990. //    /foo/bar
  991. //    Bugs:
  992. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  993. - (CString) GetDirectory
  994. {
  995.     //
  996.     //    Declare a temporary string to store the file name, clear the results, and copy
  997.     //    the actual file name into the temp string.
  998.     //
  999.     CString    lastSlash;
  1000.     CString    tempName = (CString) malloc(strlen(FileName)+1);
  1001.     [self ResetResults];
  1002.     strcpy(tempName, FileName);
  1003.     //
  1004.     //    Locate the last slash in the path name.  If we find it, make it the end of the string instad,
  1005.     //    otherwise clear the whole string.  Return it in any case.
  1006.     //
  1007.     lastSlash = strrchr(tempName, '/');
  1008.     if (lastSlash != NullCString)
  1009.     {
  1010.         lastSlash[0] = EndOfCString;
  1011.         [self StoreErrorCode:  ERR_OK AndText: "Got the directory"];
  1012.     }
  1013.     else
  1014.     {
  1015.         tempName[0]=EndOfCString;
  1016.         [self StoreErrorCode:  ERR_NODIRECTORY AndText: "There were no directory names"];
  1017.     }
  1018.     [self StoreCString: tempName];
  1019.     return tempName;
  1020. }
  1021.  
  1022.  
  1023. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1024. //    Routine:        GetPathname:
  1025. //    Parameters:    none
  1026. //    Returns:        the copy of a string containing the pathname to this file
  1027. //    Stores:        A pointer to the same string we return
  1028. //    Description:
  1029. //        This merely makes a copy of the string that we have stored for the pathname
  1030. //        and returns it to the caller.  Given the filename  /foo/bar/hotfile.rtf, this returns
  1031. //        /foo/bar/hotfile.rtf
  1032. //    Bugs:
  1033. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1034. - (CString) GetPathname
  1035. {
  1036.     CString    tempName;
  1037.     [self ResetResults];
  1038.     //
  1039.     //    Allocate the proper space for the pahtname, store the text, and return it.
  1040.     //
  1041.     tempName = (CString) malloc(strlen(FileName)+1);
  1042.     strcpy(tempName, FileName);
  1043.     [self StoreErrorCode: ERR_OK AndText: "Gotcha your path"];
  1044.     [self StoreCString: tempName];
  1045.     return tempName;
  1046. }
  1047.  
  1048.  
  1049. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1050. //    Routine:        MoveTo:
  1051. //    Parameters:    the byte position we should move to in the file
  1052. //    Returns:        self
  1053. //    Stores:        The position we moved to
  1054. //    Description:
  1055. //        This changes our position in the file to the position byteLoc bytes from the
  1056. //        beginning of the file.  The new position in the file is stored before we return.
  1057. //        Deal with exceptions being raised around the NXSeek code.  If an error occurs,
  1058. //        then we will assume that it's because we tried to move past the eof, and so we
  1059. //        set the position at the end of a file.  if an unknown exception occurs, do nothing.
  1060. //    Bugs:
  1061. //        I don't know if the assumption that an NX_illegalSeek exception necessarily means
  1062. //        we tried to go past the end of the stream.  If not, there's problems here.
  1063. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1064. - (id) MoveTo: (PositiveInteger) byteLoc
  1065. {
  1066.     Boolean result;
  1067.     [self ResetResults];
  1068.     
  1069.     if (TheFile == NULL)
  1070.         [self StoreErrorCode: ERR_FILENOTOPEN
  1071.             AndText: "The file wasn't even open!  How d'ya expect me to move around in it?!"];
  1072.     else if (AccessMode == FILE_APPEND)
  1073.         [self StoreErrorCode: ERR_FILEAPPENDONLY
  1074.             AndText: "Can not change position in a file set for only appending"];
  1075.     else
  1076.     {
  1077.         //
  1078.         //    Try to move the requested number of bytes.  If an exception occurrs,
  1079.         //    and it's a seek error, just stick us at the end quietly (assume the
  1080.         //    error was due to running over the end.  Otherwise, store
  1081.         //    an error code and leave things as they are.  
  1082.         //
  1083.         NX_DURING
  1084.             NXSeek(TheFile, byteLoc, NX_FROMSTART);
  1085.             [self StoreErrorCode: ERR_OK AndText: "Moved OK"];
  1086.         NX_HANDLER
  1087.             switch(NXLocalHandler.code)
  1088.             {
  1089.                 case NX_illegalSeek:
  1090.                     NXSeek(TheFile, 0, NX_FROMEND);
  1091.                     [self StoreErrorCode: ERR_OK AndText: "Moved to the end."];
  1092.                     FileLocation = fileAtEOF;
  1093.                     break;
  1094.                 default :
  1095.                     [self StoreErrorCode: ERR_BADADVANCE
  1096.                         AndText: "A unknown exception occurred when advancing."];
  1097.                     break;
  1098.             }
  1099.         NX_ENDHANDLER
  1100.  
  1101.         result = NXAtEOS(TheFile);
  1102.         if (result != NO)
  1103.             FileLocation = fileAtEOF;
  1104.         else if ( NXTell(TheFile) == 0)
  1105.             FileLocation = fileAtStart;
  1106.         else
  1107.             FileLocation = fileInMiddle;
  1108.  
  1109.  
  1110.         [self StorePositiveInteger: NXTell(TheFile)];
  1111.     }
  1112.     return self;
  1113. }
  1114.  
  1115.  
  1116. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1117. //    Routine:        ReadByte
  1118. //    Parameters:    none
  1119. //    Returns:        the byte we read
  1120. //    Stores:        The byte we read
  1121. //                YES if EOF was encountered
  1122. //    Description:
  1123. //        This reads a byte from our file. If all goes well, we return the byte.  Otherwise,
  1124. //        we return 0 and store an error
  1125. //    Bugs:
  1126. //        BOOL NXAtEOS(NXStream *stream)
  1127. //        Says it should not be used on streams open for writing.  What about read/write?
  1128. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1129. - (Byte) ReadByte
  1130. {
  1131.     Byte byteIn = 0;
  1132.     Boolean    result;
  1133.     
  1134.     [self ResetResults];
  1135.  
  1136.     if (TheFile == NULL)
  1137.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
  1138.     else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND))
  1139.         [self StoreErrorCode: ERR_FILEWRITEONLY
  1140.             AndText: "Can not read from a file set for only appending or writing"];
  1141.     else
  1142.     {
  1143.         result = NXAtEOS(TheFile);
  1144.         if (result != NO)
  1145.         {
  1146.             [self StoreErrorCode: ERR_EOF AndText: "End of file found"];
  1147.             FileLocation = fileAtEOF;
  1148.             [self StorePositiveInteger: 0];
  1149.             [self PutBoolean: YES Into: SECOND_RESULT];
  1150.         }
  1151.         else
  1152.         {
  1153.             byteIn = NXGetc(TheFile);
  1154.             [self StoreErrorCode: ERR_OK AndText: "Read the byte"];
  1155.             [self StorePositiveInteger: byteIn];
  1156.             [self PutBoolean: NO Into: SECOND_RESULT];
  1157.         }
  1158.     }
  1159.     return byteIn;
  1160. }
  1161.  
  1162. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1163. //    Routine:        Read:BytesInto:
  1164. //    Parameters:    the number of bytes we are to read
  1165. //                the buffer that we are to read info
  1166. //    Returns:        self
  1167. //    Stores:        The number of bytes we succesfully read
  1168. //                YES if we found EOF at the end of the current read
  1169. //    Description:
  1170. //        This attempts to read in numBytes bytes from our file into the buffer.  It does no
  1171. //        checking to assure that the buffer is of the proper size.  After reading the bytes, it
  1172. //        stores the number of bytes it succeeded in reading.  If the number of bytes written
  1173. //        is not equal to the number requested, an error is stored.
  1174. //    Bugs:
  1175. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1176. - (id) Read: (PositiveInteger) numBytes BytesInto: (ByteString) buffer
  1177. {
  1178.     PositiveInteger    BytesRead;
  1179.     Boolean            result;
  1180.     
  1181.     [self ResetResults];
  1182.  
  1183.     if (TheFile == NULL)
  1184.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
  1185.     else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND))
  1186.         [self StoreErrorCode: ERR_FILEWRITEONLY
  1187.             AndText: "Can not read from a file set for only appending or writing"];
  1188.     else
  1189.     {
  1190.         result = NXAtEOS(TheFile);
  1191.         if (result != NO)
  1192.         {
  1193.             [self StoreErrorCode: ERR_EOF AndText: "End of file found"];
  1194.             FileLocation = fileAtEOF;
  1195.             [self StorePositiveInteger: 0];
  1196.             [self PutBoolean: YES Into: SECOND_RESULT];
  1197.         }
  1198.         else
  1199.         {
  1200.             BytesRead = NXRead(TheFile, buffer, numBytes);
  1201.             //
  1202.             //    Deal with the consequences of our actions
  1203.             //
  1204.             if (BytesRead != numBytes)
  1205.                 [self StoreErrorCode: ERR_READINGERROR AndText: "Read the wrong number of bytes"];
  1206.             else
  1207.                 [self StoreErrorCode: ERR_OK AndText: "Read the bytes."];
  1208.             [self StorePositiveInteger: BytesRead];
  1209.             [self PutBoolean: NO Into: SECOND_RESULT];
  1210.         }
  1211.     }
  1212.     return self;
  1213. }
  1214.  
  1215.  
  1216. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1217. //    Routine:        WriteByte
  1218. //    Parameters:    the byte we are to write
  1219. //    Returns:        self
  1220. //    Stores:        The byte we think we wrote
  1221. //    Description:
  1222. //        This writes a specified byte to our file.  It then checks to make sure we wrote
  1223. //        that byte.  If we failed to do that, we generate an error.
  1224. //    Bugs:
  1225. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1226. - (id) WriteByte: (Byte) theByte
  1227. {
  1228.     PositiveInteger byteOut;
  1229.     
  1230.     [self ResetResults];
  1231.  
  1232.     if (TheFile == NULL)
  1233.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
  1234.     else if (AccessMode == FILE_READ)
  1235.         [self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"];
  1236.     else
  1237.     {
  1238.         byteOut = NXPutc(TheFile, theByte);
  1239.         if (byteOut == theByte) 
  1240.             [self StoreErrorCode: ERR_OK AndText: "Wrote the byte"];
  1241.         else
  1242.             [self StoreErrorCode: ERR_WROTEBADCHAR AndText: "Wrote the wrong byte"];
  1243.         [self StorePositiveInteger: byteOut];
  1244.     }
  1245.     return self;
  1246. }
  1247.  
  1248.  
  1249.  
  1250. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1251. //    Routine:        Write:BytesFrom:
  1252. //    Parameters:    the number of bytes we are to write
  1253. //                the buffer that we are to write from
  1254. //    Returns:        self
  1255. //    Stores:        The number of bytes we succesfully wrote
  1256. //    Description:
  1257. //        This writes a specified number of bytes from the buffer we have been passed to
  1258. //        the file that we manipulate.  If we fail to write all the bytes requested, we store an
  1259. //        error.  We always store the number of bytes we wrote.
  1260. //    Bugs:
  1261. //        We never flush the data out (NXFlush does naught for a memory stream)
  1262. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1263. - (id) Write: (PositiveInteger) numBytes BytesFrom: (ByteString) buffer
  1264. {
  1265.     PositiveInteger    bytesWrote;
  1266.     
  1267.     [self ResetResults];
  1268.  
  1269.     if (TheFile == NULL)
  1270.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
  1271.     else if (AccessMode == FILE_READ)
  1272.         [self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"];
  1273.     else
  1274.     {
  1275.         bytesWrote = NXWrite(TheFile, buffer, numBytes);
  1276.         //
  1277.         //    Deal with the consequences of our actions
  1278.         //
  1279.         if (bytesWrote != numBytes)
  1280.             [self StoreErrorCode: ERR_WRITINGERROR
  1281.                 AndText: "Wrote the wrong number of bytes"];
  1282.         else
  1283.             [self StoreErrorCode: ERR_OK AndText: "Wrote the bytes."];
  1284.         [self StorePositiveInteger: bytesWrote];
  1285.     }
  1286.     return self;
  1287. }
  1288.  
  1289.  
  1290. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1291. //    Method:        FileSize
  1292. //    Parameters:    none
  1293. //    Returns:        An integer reflecting the size of the file.  Set to 0 if an error occured
  1294. //    Stores:        The return value, if all went well, otherwise nothing
  1295. //    Description:
  1296. //        This determines how many bytes long the file that this object manipulates is,
  1297. //        and returns this number to the caller.
  1298. //    Bugs:
  1299. //        Only usable if the file has been opened...
  1300. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1301. - (PositiveInteger) FileSize
  1302. {
  1303.     PositiveInteger    currentLoc, eofLoc;
  1304.     
  1305.     [self ResetResults];
  1306.     if (TheFile == NULL)
  1307.     {
  1308.         [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened."];
  1309.         eofLoc = 0;
  1310.         [self StorePositiveInteger: 0];
  1311.     }
  1312.     else
  1313.     {
  1314.         currentLoc = NXTell(TheFile);
  1315.         NXSeek(TheFile, 0, NX_FROMEND);
  1316.         eofLoc = NXTell(TheFile);
  1317.         NXSeek(TheFile, currentLoc, NX_FROMSTART);
  1318.         [self StoreErrorCode: ERR_OK AndText: "Obtained size of file."];
  1319.         [self StorePositiveInteger: eofLoc];
  1320.     }
  1321.     return eofLoc;
  1322. }
  1323.  
  1324.  
  1325. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1326. //    Method:        FileInfo
  1327. //    Parameters:    none
  1328. //    Returns:        self
  1329. //    Stores:        the access mode is stored in return position 1
  1330. //                a pointer to an OS-dependant structure is in 2
  1331. //    Description:
  1332. //        This returns information to the caller that isn't accessable through other
  1333. //        methods.  At this point, it returns the access method for the file, currently being
  1334. //        used.  It also returns a pointer to a stat structure (see the man page for stat).
  1335. //        The stat structure may be replaced by a better structure in a future
  1336. //        version
  1337. //    Bugs:
  1338. //        info like file size won't be accurate to the caller.
  1339. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1340. -  FileInfo
  1341. {
  1342.      struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat));
  1343.     
  1344.     if (stat(FileName, statbuf) != 0)
  1345.         [self StoreErrorCode: ERR_CANTGETINFO AndText: "Could not get machine dep. info"];
  1346.     else
  1347.     {
  1348.         [self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
  1349.         [self PutInteger: AccessMode Into: FIRST_RESULT];
  1350.         [self CopyPointer: (Pointer) statbuf WithLength: sizeof(struct stat) Into: SECOND_RESULT];
  1351.          }
  1352.     free((char*) statbuf);
  1353.     return self;
  1354. }
  1355.  
  1356.  
  1357. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1358. //    Method:        SameFileAs
  1359. //    Parameters:    the path to another file
  1360. //    Returns:        A boolean value.  yes indicates that the pathname refers to the same
  1361. //                file this object is accessing.  no means either they are different or
  1362. //                the other file does not exist. (or that this one doesn't exist)
  1363. //    Stores:        This boolean.
  1364. //    Description:
  1365. //        Used to determine if some other file is the same as this one..  Pass the name of
  1366. //        the other file with a path, and this will check to see it if it refers to the same
  1367. //        file that it is using.  Note that this doesn't understand symbolic links, really.
  1368. //    Bugs:
  1369. //        Inadequately deals with the negative cases where one or both files might not
  1370. //        actually exist.
  1371. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1372. -  (Boolean) SameFileAs: (CString) someOtherFile
  1373. {
  1374.      struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat));
  1375.     CString     pathToFile = [self GetFullPathFrom:  someOtherFile];
  1376.     ErrorCode result;
  1377.     Boolean answer = NO;
  1378.     
  1379.     if (stat(pathToFile, statbuf) != 0)
  1380.         result = -1;
  1381.     else
  1382.     {
  1383.         if (TheInode == statbuf->st_ino)
  1384.             result = 0;
  1385.         else
  1386.             result = 1;
  1387.     }
  1388.     FreeCString(pathToFile);
  1389.     //    92.01.01    djb    Added freeing of statbuf to remove a memory leak.
  1390.     free((char*) statbuf);
  1391.     
  1392.     switch (result)
  1393.     {
  1394.         case -1:
  1395.             [self StoreErrorCode: ERR_OK AndText: "The file does not even exist!"];
  1396.             answer = NO;
  1397.             break;
  1398.         case 0:
  1399.             [self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
  1400.             answer = YES;
  1401.             break;
  1402.         case 1:
  1403.             [self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
  1404.             answer = NO;
  1405.             break;
  1406.     }
  1407.     [self StoreBoolean: answer];
  1408.     return answer;
  1409. }
  1410.  
  1411.  
  1412.  
  1413. @end